#include "StdAfx.h"
#include "VSqlData.h"

// VSqlDataColumn /////////////////////////////////////////////////////////////

//
QString VSqlDataColumn::displayName() const
{ 
	QString name = m_meta.item().displayName().value().toString();
	if (m_meta.item().isRef())
		name += "<...>";
		
	return name;
}

// VSqlDataColumnList /////////////////////////////////////////////////////////

//
VSqlDataColumnList::VSqlDataColumnList()
	:m_colIdRec(-1)
{
}

//
void VSqlDataColumnList::setFrom(const QList<VSqlMeta> &metaFieldList)
{
	clear();
	foreach (const VSqlMeta metaField,metaFieldList)
	{
		if (!metaField.item().isField())
			continue;

		VSqlDataColumn col;
			col.setMeta(metaField);
		QList<VSqlDataColumn>::append(col);
	}

	m_colIdRec = indexOf(ESqlItemType_idRec);
	Q_ASSERT(m_colIdRec >= 0);
}

//
int VSqlDataColumnList::indexOf(ESqlItemType eItemType) const
{
	int index = 0;

	foreach (const VSqlDataColumn col,*this)
	{
		if (col.meta().item().eItemType() == eItemType)
			return index;

		index++;
	}

	return -1;
}

//
VSqlSharedRecordPtr VSqlDataColumnList::newRecord() const
{
	VSqlSharedRecordPtr record(new QVariantList);
	foreach (VSqlDataColumn col,*this)
		record->append(QVariant(/*col.field().ty*/));
		
	return record;
}

// VSqlData ///////////////////////////////////////////////////////////////////

//
void VSqlData::clear()
{
	m_recordList.clear();
	m_columnList.clear();
}

//
void VSqlData::setFrom(const VSqlMeta &metaTable,const QList<VSqlMeta> &metaFieldList)
{
	clear();
	
	m_metaTable = metaTable;
	Q_ASSERT(m_metaTable.item().isTable() == true);
	m_columnList.setFrom(metaFieldList);
}

//
QSqlField VSqlData::idRecField() const
{
	if (!m_columnList.count())
		return QSqlField();
		
	return QSqlField(
		m_columnList[m_columnList.colIdRec()].meta().item().nameInDb().value().toString()
	);
}

//
VSqlSharedRecordPtr VSqlData::record_byIdRec(const QVariant &idRec)
{
	if (idRec.isNull())
		return VSqlSharedRecordPtr();
	
	int row = rowFromIdRec(idRec);
	if (row < 0)
	{
		QSqlField idRecField = this->idRecField();
		QString sqlWhere = idRecField.name() + " in(" + idRec.toString() + ")";
		QList<VSqlSharedRecordPtr> records = selectRecords(sqlWhere);
		if (!records.count())
			return VSqlSharedRecordPtr();

		m_recordList << records[0];
		return records[0];
	}
	return record_byRow(row);
}

//
QString VSqlData::record_toDisplay(const QVariant &idRec)
{
	VSqlSharedRecordPtr record = record_byIdRec(idRec);
	if (!record)
		return "<null>";

	QString s;
	foreach (const QVariant v,*record) {
		s += v.toString() + ",";
	}
	s.remove(s.count()-1,1);
		
	return s;
}

//
QList<VSqlSharedRecordPtr> VSqlData::selectRecords(const QString &sqlWhere) const
{
	QString dataTableName = m_metaTable.item().nameInDb().value().toString();
	QString idRecName = m_columnList[m_columnList.colIdRec()].meta().item().nameInDb().value().toString();
	VSqlDataMRef mRef;

	QString sRef
		= "("
		 		"select list(r." + mRef.refIdRec().name() + ") "
		+		"from "          + mRef.tableName(dataTableName) + " r "
		+		"where  r."      + mRef.idRec().name() + " = d." + idRecName
		+		"  and  r."      + mRef.refIdItem().name() + " = %1 "
		//?? "order by r.pos "
			")"
	;

	QString sFields;
	foreach (const VSqlDataColumn col,m_columnList)
	{
		VSqlItem item = *col.meta().itemPtr();
		if (item.isMRef())
			sFields += QString(sRef).arg(item.refIdItem().value().toUInt());
		
		sFields += item.nameInDb().value().toString() + ",";
	}

	sFields.remove(sFields.count()-1,1);

	QString sql
		= " select " + sFields
		+ " from "   + dataTableName + " d "
	;

	if (!sqlWhere.isEmpty())
		sql += " where " + sqlWhere;
	
	if (VSqlLog::canWrite())
		VSqlLog::write("VSqlData::selectRecords",sql);

	VSqlQuery query(m_db);
		bool isOk = query.prepare(sql);
		Q_ASSERT(isOk == true);
		isOk = query.exec();
		Q_ASSERT(isOk == true);

	QList<VSqlSharedRecordPtr> recordList;
	while (query.next())
	{
		VSqlSharedRecordPtr dataRecord = m_columnList.newRecord();
		QSqlRecord queryRecord = query.record();
		
		for (int i = 0; i < queryRecord.count(); ++i)
			(*dataRecord)[i] = queryRecord.value(i);
			
		recordList.append(dataRecord);
	}

	return recordList;
}

//
QList<VSqlSharedRecordPtr> VSqlData::selectRecords_byIdRecList(const QVariantList &idRecList) const
{
	if (idRecList.count() == 0)
		return QList<VSqlSharedRecordPtr>();

	QString sValue;
	foreach (const QVariant v,idRecList)
		sValue += v.toString() + ",";
	sValue.remove(sValue.count()-1,1);

	QString sqlWhere = idRecField().name() + " in(" + sValue + ")";
	return selectRecords(sqlWhere);
}

//
void VSqlData::resetRecordList()
{
	m_recordList.clear();
}

//
void VSqlData::setRecordList(const QList<VSqlSharedRecordPtr> &recordList) 
{ 
	m_recordList = recordList; 
}

//
bool VSqlData::updateValue(int row,int column,const QVariant &value)
{
	if (column >= m_columnList.count())
		return false;

	QString dataTableName = m_metaTable.item().nameInDb().value().toString();
	int nColIdRec = m_columnList.colIdRec();
	QSqlField fieldIdRec ( m_columnList[nColIdRec].meta().item().nameInDb().value().toString() );
	QSqlField fieldUpdate( m_columnList[column   ].meta().item().nameInDb().value().toString() );

	VSqlSharedRecordPtr record = m_recordList[row];
	fieldIdRec.setValue((*record)[nColIdRec]);
	fieldUpdate.setValue(value);

	VSqlItem itemUpdate = *m_columnList[column].meta().itemPtr();
	if (itemUpdate.isIdRec())
		return false;
		
	QSqlDatabase db(m_db);
	db.transaction();

	if (itemUpdate.isMRef())
	{
		QRegExp regExp("[0-9]+(,[0-9]+)*");//uint,uint,..
		QVariant val = value.toString().remove(QChar(' '));

		VSqlDataMRef::deleteRef(db,dataTableName,fieldIdRec.value(),itemUpdate.refIdItem().value());
		
		if (regExp.exactMatch(val.toString()))
			VSqlDataMRef::insertRef(db,dataTableName,fieldIdRec.value(),itemUpdate.refIdItem().value(),value);
		else
			val = QVariant();
		
		(*record)[column] = val;
		db.commit();
		return true;
	}
	
	VSqlQuery_update update(db);

	update.prepare
	(
		dataTableName,
		VSqlFieldList() << fieldUpdate,
		QString() = fieldIdRec.name() + " = :" + fieldIdRec.name()
	);

	if (VSqlLog::canWrite())
		VSqlLog::write("VSqlData::updateValue",update.sql());

	update.exec
	(
		VSqlFieldList() << fieldUpdate,
		VSqlFieldList() << fieldIdRec
	);

	(*record)[column] = value;
	db.commit();
	return true;
}

//
VSqlSharedRecordPtr VSqlData::insertRecord()
{
	QString tableName = m_metaTable.item().nameInDb().value().toString();
	int nColIdRec = m_columnList.colIdRec();
	QSqlField fieldIdRec( m_columnList[nColIdRec].meta().item().nameInDb().value().toString() );

	VSqlSharedRecordPtr dataRecord = m_columnList.newRecord();

	QSqlDatabase db(m_db);
	db.transaction();

	VSqlQuery_insert ins(db);
		ins.prepare(tableName, VSqlFieldList()    << fieldIdRec);
		ins.exec   (fieldIdRec,VSqlFieldPtrList() << &fieldIdRec);

	if (VSqlLog::canWrite())
		VSqlLog::write("VSqlData::insertRecord",ins.sql());

	(*dataRecord)[nColIdRec] = fieldIdRec.value();
	m_recordList.append(dataRecord);
	db.commit();
	return dataRecord;
}

//
QVariantList VSqlData::idRecListFromRowList(const VIntegerList &rowList) const
{
	int nColIdRec = m_columnList.colIdRec();
	QVariantList idRecList;
	
	foreach (int row,rowList)
	{
		VSqlSharedRecordPtr dataRecord = m_recordList[row];
		QVariant idRec = (*dataRecord)[nColIdRec];
		
		if (!idRecList.contains(idRec))
			idRecList.append(idRec);
	}
	
	return idRecList;
}

//
int VSqlData::rowFromIdRec(const QVariant &idRec) const
{
	int nColIdRec = m_columnList.colIdRec();

	for (int row = 0; row < m_recordList.count(); ++row)
	{
		QVariant rIdRec = (*m_recordList[row])[nColIdRec];
		if (rIdRec == idRec)
			return row;
	}
	
	return -1;
}

//
void VSqlData::removeRow(int row)
{
	m_recordList.removeAt(row);
}

//
void VSqlData::removeRecords(const QVariantList &idRecList) const
{
	if (!m_columnList.count() || !idRecList.count())
		return;
	
	QString dataTableName = m_metaTable.item().nameInDb().value().toString();
	int nColIdRec = m_columnList.colIdRec();
	QSqlField fieldIdRec( m_columnList[nColIdRec].meta().item().nameInDb().value().toString() );

	QString sIdRec;
	foreach (const QVariant idRec,idRecList)
		sIdRec += idRec.toString() + ",";
		
	sIdRec.remove(sIdRec.count()-1,1);
		
	QSqlDatabase db(m_db);
	db.transaction();
	VSqlQuery_delete del(db);

	del.prepare
	(
		dataTableName,
		QString() 
			= fieldIdRec.name() + " in(" + sIdRec + ")"
	);

	if (VSqlLog::canWrite())
		VSqlLog::write("VSqlData::removeRecords",del.sql());

	del.exec(VSqlFieldList());

	VSqlDataMRef::deleteRef(db,dataTableName,idRecList);
	db.commit();
}
